package cn.lizemin.fakedata.security.controller;

import cn.lizemin.fakedata.common.BusinessException;
import cn.lizemin.fakedata.common.RespBean;
import cn.lizemin.fakedata.common.ResultEnum;
import cn.lizemin.fakedata.constant.SecurityConstant;
import cn.lizemin.fakedata.security.SecurityContextHolder;
import cn.lizemin.fakedata.security.UserDetails;
import cn.lizemin.fakedata.security.UserDetailsService;
import cn.lizemin.fakedata.security.entity.LoginRequest;
import cn.lizemin.fakedata.utils.JSON;
import cn.lizemin.fakedata.utils.ServletUtil;
import org.apache.tomcat.util.buf.HexUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.security.SecureRandom;

/**
 * @author lzm
 * @Date 2024/7/7
 * @description
 */
@RestController
public class LoginController {

    private static final Logger log = LoggerFactory.getLogger(LoginController.class);

    @Autowired
    private UserDetailsService userDetailsService;

    @PostMapping("/login")
    public RespBean login(@RequestBody LoginRequest loginRequest, HttpServletResponse response) {
        log.info("接收到用户的请求参数为：{}", JSON.toJson(loginRequest));
        String username = loginRequest.getUsername();
        String password = loginRequest.getPassword();
        // 根据用户名去查询用户的密码，然后再做密码比对
        UserDetails user = userDetailsService.loadUserByUsername(username);
        if (password.equals(user.getPassword())) {
            log.info("用户{}登录成功", username);
            String token = generateToken();
            HttpSession session = ServletUtil.getSession();
            session.setAttribute(token, user);
            SecurityContextHolder.setCurrentUser(user);
            Cookie cookie = new Cookie(SecurityConstant.TOKEN_NAME, token);
            cookie.setMaxAge(60 * 60);
            response.addCookie(cookie);
            return RespBean.success(ResultEnum.LOGIN_SUCCESS);
        }
        throw new BusinessException(ResultEnum.USER_NOT_EXITS);
    }

    private static String generateToken() {
        SecureRandom random = new SecureRandom("123".getBytes());
        byte[] randomBytes = new byte[16];
        random.nextBytes(randomBytes);
        return HexUtils.toHexString(randomBytes);
    }

    @GetMapping("/getUserInfo")
    public RespBean getUserInfo() {
        UserDetails user = SecurityContextHolder.getCurrentUser();
        return RespBean.success(user);
    }

    /**
     * 注销登录后，就要清理th readLocal中的信息了，
     * 可是万一用户永远不注销登录怎么办，而线程是复用的，那我会不会拿到其他用户的信息？
     *      答案是：不会，因为认证失败后，也会清理threadLocal中的用户信息
     *      我通过自己的用户名和密码登录成功后，则是将自己的用户信息给放到这个线程中，这样就覆盖了上个用户的信息
     */
    @GetMapping("/logout")
    public RespBean logout(HttpServletRequest request) {
        String username = SecurityContextHolder.getCurrentUser().getUsername();
        log.info("用户{}已经注销登录", username);
        SecurityContextHolder.clear();
        Cookie[] cookies = request.getCookies();
        String token = "";
        for (Cookie cookie : cookies) {
            if (SecurityConstant.TOKEN_NAME.equals(cookie.getName())) {
                token = cookie.getValue();
                break;
            }
        }
        HttpSession session = request.getSession();
        session.removeAttribute(token); // 如果不从session中移除token，那么用户注销后还是可以请求接口
        return RespBean.success().message("注销登录成功！");
    }

    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            System.out.println(generateToken());
        }
    }


}
